總之,依賴注入就是為了使程式更有彈性,把class A需要的class B從外面傳入。好的講完了,可以收工了。什麼?還不到300字?好吧,繼續來混一些字數...
雖然沒有狀態管理這麼火熱,但依賴注入也是在Flutter社群經常被提到的議題,也經常有新套件出現。這有一部份是因為,Flutter界現在還沒有一個功能完善、穩定維護、被廣泛使用的依賴注入框架出現。另一部份則是因為,依賴注入這個詞,在Flutter社群有時候被挪用到了某些不相關的場景中。依賴注入到底是什麼?要解決什麼問題?為什麼我們須要它?這次就讓我們來一探究竟。
首先來看看這個簡單的例子:
class MusicRecommender {
MusicRepository repo;
MusicRecommender() {
repo = MusicRepository();
}
List<Music> recommendMusic(User user) {
return repo.getMusics()
.where((music) => user.taste.contains(music.genre))
.toList();
}
}
class MusicRepository {
List<Music> getMusics() => // get musics from Firebase
}
我們有個音樂APP(可能是下一個Spotify!),裡面有個推薦音樂的功能。因為我們熱愛OOP,自然把它包成了MusicRecommender。接著,既然我們要推薦音樂,我們當然要有個音樂庫MusicRepository,才能取得音樂來推薦。也就是說我們的MusicRecommender依賴MusicRepository才能運作。看起來挺好的,這能出什麼問題?
想像一下以下三個情境:
聰明的你看著這三個情境,心裡是不是已經在吶喊著答案了呢?「介面!介面!拜託你使用介面!」
沒錯,如果今天我們的MusicRepository是個介面:
abstract class MusicRepository {
List<Music> getMusics();
}
class FirebaseMusicRepository implement MusicRepository {
List<Music> getMusics() => // get music from Firebase
}
class MySqlMusicRepository implement MusicRepository {
List<Music> getMusics() => // get music from MySql
}
class MockMusicRepository implement MusicRepository {
List<Music> getMusics() => // get music from predefined variable
}
從類別的相依性來看,透過定義一個介面,使你的類別A不再依賴另一個類別B,而是兩個類別都依賴介面,進而降低了彼此的耦合程度,此一過程正是S.O.L.I.D中的依賴反轉原則(Dependency Inversion Principle, DIP)。
而從程式執行的流程來看,類別A不主動去尋找自己需要的類別B,而是被動的由外界輸入,則可以說是一種控制反轉原則(Inversion of Control, IoC)的體現。
這裡的「控制」反轉可能會比「依賴」反轉更難理解一點,到底什麼控制什麼?又是怎麼個反轉法?沒關係你並不孤單,事實上就連Martin Fowler大神也曾經有這個疑問(這篇文章很長,但強烈建議能看的人就花點時間慢慢把它看完,相信會比聽我在這裡鬼扯更有幫助)。這也沒辦法,因為IoC一開始指的並不是這件事...
很久很久以前,程式都是在命令列上執行的,而我們的音樂推薦功能可能長這樣:
>who are you?
:Joshua
>What music do you like?
:classical music
>What music do you really like?
:anime songs
>Ok here are songs that you might like...
整個流程是由你來控制的,由你決定什麼時候問什麼問題、呼叫什麼函數、要求什麼輸入,給出什麼結果。
然而,自從有了GUI以後,我們發現我們可以一次問好幾個問題,而且可以隨時改變我們的結果:
這時候程式的流程變成主要是由框架的事件迴圈(event loop)來決定的。我們負責去實作框架提供的介面或抽象類別(Widget),告訴框架我們想顯示什麼(build函數),怎麼跟使用者互動(onPressed函數),由框架決定什麼時候去呼叫你的程式碼。
這樣講應該更符合控制反轉這幾個字了吧?這也就是為什麼有時候IoC也被戲稱為好萊塢原則(Don't call us, we call you.)。
總之IoC這個詞的源頭來自於框架開始被大量使用,而程式的控制權從開發者本身轉移到框架身上的時代。至於為什麼十五年前開始,有許多Dependency Injection或Service Locator套件的作者把它們的套件叫做"IoC" Container,就比較難以考究了。
這裡提這個小故事主要是為了接下來終於要進入的依賴注入部份,因為在十五年後的今天,依賴注入這個詞,在Flutter社群同樣被誤用(挪用?演化?端看你對這件事的態度多負面)到了其它莫名其妙的場景上。
欲知詳情,請待下回分曉...(沒錯這整篇都是前言,到現在還沒進入DI的部份,但是相信我這一切都很有關係)